package com.xishu.util;

import org.apache.commons.lang.ClassUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeanUtils;

import java.io.*;
import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;

public class ClassUtil implements Tools {
    private static Logger logger = LoggerFactory.getLogger(ClassUtil.class);

    private static ClassUtil instance = new ClassUtil();

    private ClassUtil() {
        this.instance = instance;
    }

    public static ClassUtil getInstance() {
        return instance;
    }

    /**
     * 获取一个字段
     *
     * @param tclass
     * @param fieldName
     * @return
     */
    public Field getField(Class tclass, String fieldName) {
        List<Field> fieldList = getAllField(tclass).stream().filter(f -> {
            return equals(f.getName(), fieldName);
        }).collect(Collectors.toList());

        if (isNotEmpty(fieldList)) {
            Field field = fieldList.get(0);
            field.setAccessible(true);
            return field;
        }

        logger.info("can not find the field {}, in class {}", fieldName, tclass.getName());

        return null;
    }

    /**
     * 对象转字节数组
     */
    public static byte[] objectToBytes(Object obj) throws IOException {
        ByteArrayOutputStream out = new ByteArrayOutputStream();
        ObjectOutputStream sOut = new ObjectOutputStream(out);
        sOut.writeObject(obj);
        sOut.flush();
        byte[] bytes = out.toByteArray();
        return bytes;
    }

    /**
     * 字节数组转对象
     */
    public static Object bytesToObject(byte[] bytes) throws IOException, ClassNotFoundException {
        ByteArrayInputStream in = new ByteArrayInputStream(bytes);
        ObjectInputStream sIn = new ObjectInputStream(in);
        return sIn.readObject();
    }

    /**
     * 克隆一个对象
     *
     * @param o
     * @return
     */
    public static Object clone(Object o) throws Exception {
        byte[] bytes = objectToBytes(o);
        return bytesToObject(bytes);
    }

    /**
     * 获取所有的字段
     *
     * @param tclass
     * @return
     */
    public static List<Field> getAllField(Class tclass) {
        ArrayList<Field> list = new ArrayList<>();
        ArrayList<String> fieldNameList = new ArrayList<>();
        Field[] fields = tclass.getFields();
        for (Field field : fields) {
            field.setAccessible(true);
            list.add(field);
            fieldNameList.add(field.getName());
        }

        Field[] declaredFields = tclass.getDeclaredFields();
        for (Field declaredField : declaredFields) {
            if (fieldNameList.contains(declaredField.getName())) {
                continue;
            }

            declaredField.setAccessible(true);
            list.add(declaredField);
        }

        return list;
    }

    /**
     * 获取所有的字段，忽略某些字段
     *
     * @param tclass
     * @param ignoreFieldList
     * @return
     */
    public static List<Field> getAllField(Class tclass, String... ignoreFieldList) {
        return getAllField(tclass).stream().filter(f -> !Arrays.asList(ignoreFieldList).contains(f.getName())).collect(Collectors.toList());
    }

    /**
     * 获取所有的字段，忽略某些字段
     *
     * @param object
     * @param ignoreFieldList
     * @return
     */
    public static List<Field> getAllField(Object object, String... ignoreFieldList) {
        return getAllField(object).stream().filter(f -> !Arrays.asList(ignoreFieldList).contains(f.getName())).collect(Collectors.toList());
    }

    /**
     * 获取所有的字段
     *
     * @param o
     * @return
     */
    public static List<Field> getAllField(Object o) {
        return getAllField(o.getClass());
    }

    /**
     * 打印日志
     *
     * @param o
     * @return
     */
    public static void printerObject(Object o) {
        List<Field> allField = getAllField(o);
        for (Field field : allField) {
            field.setAccessible(true);
            try {
                logger.info("field name is {} type is {} value is {}", field.getName(), field.getType().getName(), field.get(o));
            } catch (IllegalAccessException e) {
                logger.error("e", e);
            }
        }
    }

    /**
     * 获取字段的名称
     *
     * @param field
     * @return
     */
    public static String getFiledTypeName(Field field) {
        return field.getType().getName();
    }

    /**
     * 字段的类型是否是boolean类型
     *
     * @param fileType
     * @return
     */
    public static boolean isFieldBoolean(String fileType) {
        return "java.lang.Boolean".equals(fileType);
    }

    /**
     * 字段的类型是否是Integer类型
     *
     * @param fileType
     * @return
     */
    public static boolean isFieldInteger(String fileType) {
        return "java.lang.Integer".equals(fileType);
    }

    /**
     * 字段的类型是否是Long类型
     *
     * @param fileType
     * @return
     */
    public static boolean isFieldLong(String fileType) {
        return "java.lang.Long".equals(fileType);
    }

    /**
     * 字段的类型是否是Double类型
     *
     * @param fileType
     * @return
     */
    public static boolean isFieldDouble(String fileType) {
        return "java.lang.Double".equals(fileType);
    }

    /**
     * 字段的类型是否是Float类型
     *
     * @param fileType
     * @return
     */
    public static boolean isFieldFloat(String fileType) {
        return "java.lang.Float".equals(fileType);
    }

    /**
     * 字段的类型是否是String类型
     *
     * @param fileType
     * @return
     */
    public static boolean isFieldString(String fileType) {
        return "java.lang.String".equals(fileType);
    }

    /**
     * 是否是list
     *
     * @param fieldType
     * @return
     */
    public static boolean isFieldList(String fieldType) {
        return "java.util.List".equals(fieldType);
    }

    /**
     * 字段的类型是否是boolean类型
     *
     * @param field
     * @return
     */
    public static boolean isFieldBoolean(Field field) {
        return "java.lang.Boolean".equals(getFiledTypeName(field));
    }

    /**
     * 字段的类型是否是Integer类型
     *
     * @param field
     * @return
     */
    public static boolean isFieldInteger(Field field) {
        return "java.lang.Integer".equals(getFiledTypeName(field));
    }

    /**
     * 字段的类型是否是Long类型
     *
     * @param field
     * @return
     */
    public static boolean isFieldLong(Field field) {
        return "java.lang.Long".equals(getFiledTypeName(field));
    }

    /**
     * 字段的类型是否是Double类型
     *
     * @param field
     * @return
     */
    public static boolean isFieldDouble(Field field) {
        return "java.lang.Double".equals(getFiledTypeName(field));
    }

    /**
     * 字段的类型是否是Float类型
     *
     * @param field
     * @return
     */
    public static boolean isFieldFloat(Field field) {
        return "java.lang.Float".equals(getFiledTypeName(field));
    }

    /**
     * 字段的类型是否是String类型
     *
     * @param field
     * @return
     */
    public static boolean isFieldString(Field field) {
        return "java.lang.String".equals(getFiledTypeName(field));
    }

    public static boolean isAssignable(Class cls, Class toClass) {
        boolean assignable = ClassUtils.isAssignable(cls, toClass);
        if (assignable) {
            return true;
        }

        if (cls.getTypeName().equals("java.lang.Long") && toClass.getTypeName().equals("java.math.BigInteger")) {
            return true;
        }

        if (cls.getTypeName().equals("java.lang.Long") && toClass.getTypeName().equals("java.lang.Integer")) {
            return true;
        }

        return false;
    }

    /**
     * 只复制不为空的值到目标对象
     *
     * @param src
     * @param desc
     */
    public <T> void copyExistValue(T src, T desc) {
        //过滤出哪些字段不能修改
        List<String> notModifyFields = Arrays.stream(src.getClass().getDeclaredFields()).filter(f -> {
            try {
                f.setAccessible(true);
                return f.get(src) == null;
            } catch (IllegalAccessException e) {
                return true;
            }
        }).map(f -> f.getName()).collect(Collectors.toList());

        BeanUtils.copyProperties(src, desc, notModifyFields.toArray(new String[notModifyFields.size()]));
    }

    /**
     * 过滤出值是空的字段
     *
     * @param object
     * @return
     */
    public static List<Field> filterEmptyField(Object object) {
        return getAllField(object).stream().filter(field -> {
            try {
                field.setAccessible(true);
                return field.get(object) == null;
            } catch (IllegalAccessException e) {
                logger.error("e", e);
                return false;
            }
        }).collect(Collectors.toList());
    }

    /**
     * 获取注解的值，只适用于只有一个注解的情况
     *
     * @param object
     * @param annotation
     * @return
     */
    public <T extends Annotation> Object getAnnotationValue(Object object, Class<T> annotation) throws Exception {
        List<Field> fieldList = getAllField(object).stream().filter(field -> field.getAnnotation(annotation) != null).collect(Collectors.toList());
        if (isEmpty(fieldList)) {
            logger.info("do not find the annotation {}", annotation.getName());
            return null;
        }

        return fieldList.get(0).get(object);
    }

    /**
     * 找到注解对应的字段
     *
     * @param object
     * @param annotation
     * @param <T>
     * @return
     */
    public <T extends Annotation> List<Field> findFieldList(Object object, Class<T> annotation) {
        List<Field> fieldList = getAllField(object).stream().filter(field -> field.getAnnotation(annotation) != null).collect(Collectors.toList());
        return fieldList;
    }
}
